vlwkaos' digital garden

Type-Challeneges 중간 난이도

[[Type-Challeneges 쉬움 난이도]]

Get Return Type

ReturnType<T> 구현하기

type MyReturnType<T> = T extends (...args: any[]) => infer B ? B : never;

Omit

Omit<T, K> 구현하기

type MyOmit<T, K> = {
  [key in keyof T as key extends K ? never : key]: T[key];
}

여기서 as 의 역할, key에 대한 조건을 후술할 수 있도록 다리 역할을 한다. as 가 없으면 일단 property로 인식되기 때문에 이후에 오는 conditional이 의도대로 동작하지 않는다.

Readonly

T의 속성중 K를 readonly로 설정하는 타입 구현, K가 주어지지 않으면 전부 readonly

type MyReadonly2<T, K = keyof T> = {
  readonly [key in keyof T as key extends K ? key: never]: T[key];
} & {
  [key in keyof T as key extends K ? never : key]: T[key];
}

Omit<T,K> 을 이용하여 작성할 수 있다. generic작성과 마찬가지로 타입이 명시되지 않은 경우 기본값을 지정해줄 수 있다.

Deep Readonly

오브젝트와 오브젝트 내 모든 오브젝트를 재귀적으로 readonly로 만드는 제너릭 타입형을 구현하라

type DeepReadonly<T> = {
  readonly [key in keyof T]: T[key] extends Record<string, unknown> ? DeepReadonly<T[key]> : T[key]; 
};

Record<string, unknown> vs. Record<string, any> vs. object ???

Tuple to Union

generic TupleToUnion<T> 구현. 타입 조건문 사용시 분배 법칙에 의해 각 T[number]의 Union으로 완성된다.

type TupleToUnion<T> = T extends any[] ? T[number]: never;

Chainable Options

Chaining을 통해서 속성의 확장이 가능한 타입/클래스 만들기. get을 이용하여 완성된 형태를 반환

type Chainable<T extends {} = {}> = {
  option<TKey extends string, TVal>(key: TKey, value: TVal): Chainable<{
    [key in keyof T as key extends TKey ? never: key]: T[key];
  } & {
    [key in TKey]: TVal;
  }>
  get(): IntersectionMerge<T>
}

type IntersectionMerge<Tinter> = Tinter extends Record<string, any> ? {
  [key in keyof Tinter]: Tinter[key]
} : never;


declare const config: Chainable

const result = config
  .option('foo', 123)
  .option('name', 'type-channelges')
  .option('bar', {value: 'Hello World'})
  • 동일한 PropertyKey일 때의 처리
  • Intersection을 합치는 형태로 마무리한다.

Last of Array

type Last<T extends any[]> = T extends [...infer _, infer Last] ? Last : T[0]

Pop

type Pop<T extends any[]> = T extends [...infer R, any] ? R : []
Type-Challeneges 중간 난이도